package org.richin.reflection.util;

import java.lang.reflect.Type;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;

/**
 * 该类表示为一个泛型类型。
 *
 * @author Fuchun
 * @version 1.0, 2009-6-15
 */
public abstract class TypeClass<T> {

    /* 原始类型。 */
    final Class<? super T> rawType;

    /* 该泛型类型要构造的类型。 */
    final Type type;

    /**
     * 构造一个新的 {@code Type} 类型对象。
     */
    @SuppressWarnings("unchecked")
    protected TypeClass() {
        type = getSuperclassTypeParameter(getClass());
        rawType = (Class<? super T>) getRawType(type);
    }

    /**
     * 构造一个给定类型的 {@code TypeClass} 对象。
     *
     * @param type 给定的 {@code Type} 类型。
     */
    @SuppressWarnings("unchecked")
    protected TypeClass(Type type) {
        this.rawType = (Class<? super T>) getRawType(nonNull(type, "type"));
        this.type = type;
    }

    /* 检查泛型类型是否为空的便捷方法 */
    static <T> T nonNull(T o, String message) {
        if (o == null) {
            throw new NullPointerException(message);
        }
        return o;
    }

    /**
     * 返回给定子类类型的直接父类的参数类型。
     *
     * @param subclass 给定的子类。
     * @return 给定子类类型的直接父类的参数类型。
     */
    static Type getSuperclassTypeParameter(Class<?> subclass) {
        Type superclass = subclass.getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("缺少类型参数！");
        }
        return ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }

    /* 返回给定类型的原始类型或接口。 */
    private static Class<?> getRawType(Type type) {
        if (type instanceof Class<?>) {
            // type 是一个普通的类
            return (Class<?>) type;
        } else if (type instanceof ParameterizedType) { // 参数化类型
            ParameterizedType paramType = (ParameterizedType) type;

            Type rawType = paramType.getRawType();
            if (rawType instanceof Class<?>) return (Class<?>) rawType;
            throw buildUnexpectedTypeError(rawType, Class.class);
        } else if (type instanceof GenericArrayType) { // 数组类型
            GenericArrayType genericArrayType = (GenericArrayType) type;

            Object rawArrayType = Array.newInstance(getRawType(genericArrayType
                    .getGenericComponentType()), 0);
            return rawArrayType.getClass();
        } else {
            throw buildUnexpectedTypeError(type, ParameterizedType.class, GenericArrayType.class);
        }
    }

    /**
     * 返回给定的原始类型。
     *
     * @return 给定的原始类型。
     */
    public Class<? super T> getRawType() {
        return rawType;
    }

    /**
     * 返回给定泛型的 {@code Type} 实例。
     *
     * @return 给定泛型的 {@code Type} 实例。
     */
    public Type getType() {
        return type;
    }

    /* 构造无法判断类型的断言失败对象。 */
    private static AssertionError buildUnexpectedTypeError(Type t, Class<?>... expected) {
        StringBuilder buffer = new StringBuilder("无法预知的类型。现可知的类型有 ");
        for (Class<?> c : expected) {
            buffer.append(c.getName()).append(", ");
        }
        buffer.append("但是得到 ").append(t.getClass().getName());
        buffer.append(", 类型表示的是：").append(t.toString());
        return new AssertionError(buffer.toString());
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return type.hashCode();
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof TypeClass<?>)) return false;
        TypeClass<?> tc = (TypeClass<?>) o;
        return type.equals(tc.type);
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return type instanceof Class<?> ? ((Class<?>) type).getClass().getName() : type.toString();
    }

    public static TypeClass<?> get(Type type) {
        return new SimpleTypeClass<Object>(type);
    }

    public static <T> TypeClass<T> get(Class<T> type) {
        return new SimpleTypeClass<T>(type);
    }

    private static class SimpleTypeClass<T> extends TypeClass<T> {

        public SimpleTypeClass(Type type) {
            super(type);
        }
    }
}
